package com.meevle.dao;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.Order;
import org.red5.logging.Red5LoggerFactory;
import org.slf4j.Logger;

import com.meevle.business.PersistenceManagerImpl;
import com.meevle.utilities.HibernateManager;
import com.meevle.vo.SetVO;
import com.meevle.vo.SlotVO;
import com.meevle.vo.UserVO;

/*
 * Wat met tracks en projecten en users en instrumenten?
 */

@Entity
public class SetDAO implements Serializable{

	private static final long serialVersionUID = 1L;
	private static Logger log = Red5LoggerFactory.getLogger(SetDAO.class, "Meevle");
	
	private long id;
	private String name;
	
	
	
	private List<SlotDAO> slots = new ArrayList<SlotDAO>();
	
	
	private UserDAO owner;
	
	
	public SetDAO()
	{}

	public SetDAO(String name, UserDAO owner)
	{
		setName(name);
		setOwner(owner);
	}
	
	@Id @GeneratedValue
	public long getId() {
		return id;
	}

	private void setId(long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@ManyToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
	@JoinTable(	
			name = "SlotDAO_SetDAO",
			joinColumns = {@JoinColumn(name = "set_id")},
			inverseJoinColumns = {@JoinColumn(name = "slot_id")}
				)
	public List<SlotDAO> getSlots() {
		return slots;
	}
	
	public void setSlots(List<SlotDAO> tracks) {
		this.slots = tracks;
	}
	
	

	@ManyToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
	public UserDAO getOwner() {
		return owner;
	}

	public void setOwner(UserDAO owner) {
		this.owner = owner;
	}
	
	/**
	 * Get all the tracks of a certain project
	 * @param project
	 * @return
	 * @throws IllegalArgumentException if such a project doesn't exist in the DB
	 * @throws Exception If something went wrong during the DB access
	 */
	public static List<SlotDAO> getSlotsBySet(SetVO project) throws IllegalArgumentException, Exception
	{
		List<SlotDAO> trackDaos = new ArrayList<SlotDAO>();
		
		SetDAO p;
		try {
			p = getSet(project);
		} catch (IllegalArgumentException e) {
			throw e;
		} catch (Exception e) {
			throw e;
		}
		
		trackDaos = p.getSlots();
		
		return trackDaos;
	}
	
//	/**
//	 * Returns a project with this id
//	 * @param projectId
//	 * @return
//	 * @throws IllegalArgumentException if such a project doesn't exist in the DB
//	 */
//	private static ProjectDAO getProjectByID(long projectId)throws IllegalArgumentException
//	{
//		ProjectDAO pr = null;
//		pr = (ProjectDAO)HibernateManager.getInstance().getSessionFactory().openSession().createQuery("from ProjectDAO project where id = :id").setLong("id", projectId).uniqueResult();
//		if(pr==null)
//		{
//			log.error("Someone is using a project that doesn't exist with id:\n" + projectId);
//			throw new IllegalArgumentException("This project does not exist");
//		}
//		return pr;
//	}
	
	/**
	 * Get a com.meevle.dao.ProjectDAO object from a com.meevle.vo.ProjectVO object
	 * @param set The project
	 * @return a com.meevle.dao.ProjectDAO object if such a project exists, i.e. if there is such a project 
	 * @throws IllegalArgumentException if such an object doesn't exist in the DB
	 * @throws Exception If something went wrong during the DB access
	 */
	public static SetDAO getSet(SetVO set) throws IllegalArgumentException, Exception
	{
		SetDAO pr = null;
		try {
			pr = PersistenceManagerImpl.getSet(set);
		} catch (Exception e) {
			throw e;
		}
//		Session session = HibernateManager.getInstance().getSession();
//		pr = (ProjectDAO)session
//				.createQuery("from ProjectDAO project where name = :name AND owner.username = :username")
//				.setString("name", project.getName() )
//				.setString("username", project.getOwner().getUsername())
//				.uniqueResult();
				
		if(pr==null)
		{
			log.error("Someone is using a project that doesn't exist with name:\n" + set.getName());
			throw new IllegalArgumentException("This project does not exist");
		}
		
		return pr;
	}

	/**
	 * Inserts a new Project into the DB
	 * @param project
	 * @throws Exception if something went wrong with or during the insertion in the DB
	 */
	public static void persist(SetDAO project)throws Exception
	{
		try{
			PersistenceManagerImpl.persist(project);
////			Session session = HibernateManager.getInstance().getSessionFactory().openSession();
//			Session session = HibernateManager.getInstance().getSession();
//			session.beginTransaction();
//			session.saveOrUpdate(project);
//			session.getTransaction().commit();
////			session.close();
		}catch(Exception e)
		{
			log.error("Inserting a new project into the DB failed\n" + e.getMessage());
			throw new Exception("Something went wrong during the insertion of your account. Please try again later.");
		}
	}
	
	/**
	 * Get a list of all the projects in the database.
	 * @return a <code>List</code> of all projects
	 */
	public static List<SetDAO> getAll()throws Exception
	{
		List<SetDAO> ret;
		try {
			ret = PersistenceManagerImpl.getAllSets();
		} catch (Exception e) {
			throw e;
		}
		return ret;
	}
	
	/**
	 * Get all projects with a certain name
	 * @param projectname The name of the projects
	 * @return A <code>List</code> of all projects with that particular name
	 * @throws HibernateException When something went wrong during the database access
	 * @throws Exception When something else than the databaseaccess went wrong
	 */
	public static List<SetDAO> getAllSetsByName(String projectname)throws HibernateException, Exception
	{
		List<SetDAO> projects;
		try {
			projects = PersistenceManagerImpl.getAllProjectsByName(projectname);
		} catch (HibernateException e) {
			log.error("Troubles with accessing the database when trying to get all the projects by name: " + projectname);
			e.printStackTrace();
			throw new Exception("There were troubles accessing the Database whe trying to handle you request. Please try again");
		} catch (Exception e) {
			log.error(e.getMessage());
			e.printStackTrace();
			throw new Exception("There were problems trying to handle you request. Please try again");
		}
		return projects;
	}
	
	/**
	 * Update a project
	 * @param project The project you'd like to update
	 * @throws Exception If there was a problem with the update
	 */
	public static void update(SetVO oldVo, SetVO newVo)throws Exception
	{
		try {
			PersistenceManagerImpl.updateSet(oldVo, newVo);
		} catch (Exception e) {
			throw e;
		}
	}
	
	/**
	 * Get a subset of all the sets
	 * @param startIndex Index of the first set
	 * @param numberOfItems Number of sets that being returned
	 * @return A <code>List</code> of <code>SetDAO</code> objects
	 * @throws HibernateException When something went wrong during the database access
	 * @throws Exception When something else than the databaseaccess went wrong
	 */
	public static List<SetDAO> getAllSetsFromTo(int startIndex, int numberOfItems, String howToOrder)throws HibernateException, Exception
	{
				
		List<SetDAO> setDaos = new ArrayList<SetDAO>();
		
		Criteria crit;
		try {
			Session session = HibernateManager.getInstance().getSession();
			crit = session.createCriteria(SetDAO.class);
			crit.setFirstResult(startIndex);
			crit.setMaxResults(numberOfItems);
			crit.addOrder(Order.asc(howToOrder));
		} catch (HibernateException e) {
			log.error("Troubles with accessing the database when trying to get a subset of the sets" );
			e.printStackTrace();
			throw new Exception("There were troubles accessing the Database while trying to handle your request. Please try again");
		} catch (Exception e) {
			log.error(e.getMessage());
			e.printStackTrace();
			throw new Exception("There were problems trying to handle your request. Please try again");
		}
		
		setDaos = (List<SetDAO>)crit.list();
		
		return setDaos;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + (int) (id ^ (id >>> 32));
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime * result + ((owner == null) ? 0 : owner.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		SetDAO other = (SetDAO) obj;
		if (id != other.id)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (owner == null) {
			if (other.owner != null)
				return false;
		} else if (!owner.equals(other.owner))
			return false;
		return true;
	}

	public static List<SetDAO> getAllSetsByUser(UserVO ownerVo) throws Exception
	{
		List<SetDAO> sets;
		try {
			sets = PersistenceManagerImpl.getAllProjectsByUserName(ownerVo.getUsername());
		} catch (HibernateException e) {
			log.error("Troubles with accessing the database when trying to get all the projects by username: " + ownerVo.getUsername());
			e.printStackTrace();
			throw new Exception("There were troubles accessing the Database whe trying to handle you request. Please try again");
		} catch (Exception e) {
			log.error(e.getMessage());
			e.printStackTrace();
			throw new Exception("There were problems trying to handle you request. Please try again");
		}
		return sets;
	}

	
	
	/*(id: auto-increment) 
	name (50) varchar (a-z),(A-Z), (0-9), -, _, " " 
	genre (foreign key genre_id) 
	instrumenten (foreign key instrument_id) 
	tempo int BPM (0-999) 
	scale (5) varchar (C - B); 
	beatsperbar (5) (0-99)/(0-99) 
	owner (foreign key user_id) alle tracks van een project geven zicht over de members van een track
	*/
}
